
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
// 
//   http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

/**
 * AUTO-GENERATED FILE. DO NOT MODIFY.
 */

// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
// 
//   http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.
import { __extends } from 'tslib';
import BoundingRect from 'zrender/lib/core/BoundingRect.js';
import * as matrix from 'zrender/lib/core/matrix.js';
import * as graphic from '../../util/graphic.js';
import { createTextStyle } from '../../label/labelStyle.js';
import * as layout from '../../util/layout.js';
import TimelineView from './TimelineView.js';
import TimelineAxis from './TimelineAxis.js';
import { createSymbol, normalizeSymbolOffset, normalizeSymbolSize } from '../../util/symbol.js';
import * as numberUtil from '../../util/number.js';
import { merge, each, extend, isString, bind, defaults, retrieve2 } from 'zrender/lib/core/util.js';
import OrdinalScale from '../../scale/Ordinal.js';
import TimeScale from '../../scale/Time.js';
import IntervalScale from '../../scale/Interval.js';
import { parsePercent } from 'zrender/lib/contain/text.js';
import { makeInner } from '../../util/model.js';
import { getECData } from '../../util/innerStore.js';
import { enableHoverEmphasis } from '../../util/states.js';
import { createTooltipMarkup } from '../tooltip/tooltipMarkup.js';
var PI = Math.PI;
var labelDataIndexStore = makeInner();

var SliderTimelineView =
/** @class */
function (_super) {
	__extends(SliderTimelineView, _super);

	function SliderTimelineView() {
		var _this = _super !== null && _super.apply(this, arguments) || this;

		_this.type = SliderTimelineView.type;
		return _this;
	}

	SliderTimelineView.prototype.init = function (ecModel, api) {
		this.api = api;
	};
	/**
   * @override
   */

	SliderTimelineView.prototype.render = function (timelineModel, ecModel, api) {
		this.model = timelineModel;
		this.api = api;
		this.ecModel = ecModel;
		this.group.removeAll();

		if (timelineModel.get('show', true)) {
			var layoutInfo_1 = this._layout(timelineModel, api);

			var mainGroup_1 = this._createGroup('_mainGroup');

			var labelGroup = this._createGroup('_labelGroup');

			var axis_1 = this._axis = this._createAxis(layoutInfo_1, timelineModel);

			timelineModel.formatTooltip = function (dataIndex) {
				var name = axis_1.scale.getLabel({
					value: dataIndex
				});
				return createTooltipMarkup('nameValue', {
					noName: true,
					value: name
				});
			};

			each(['AxisLine', 'AxisTick', 'Control', 'CurrentPointer'], function (name) {
				this['_render' + name](layoutInfo_1, mainGroup_1, axis_1, timelineModel);
			}, this);

			this._renderAxisLabel(layoutInfo_1, labelGroup, axis_1, timelineModel);

			this._position(layoutInfo_1, timelineModel);
		}

		this._doPlayStop();

		this._updateTicksStatus();
	};
	/**
   * @override
   */

	SliderTimelineView.prototype.remove = function () {
		this._clearTimer();

		this.group.removeAll();
	};
	/**
   * @override
   */

	SliderTimelineView.prototype.dispose = function () {
		this._clearTimer();
	};

	SliderTimelineView.prototype._layout = function (timelineModel, api) {
		var labelPosOpt = timelineModel.get(['label', 'position']);
		var orient = timelineModel.get('orient');
		var viewRect = getViewRect(timelineModel, api);
		var parsedLabelPos; // Auto label offset.

		if (labelPosOpt == null || labelPosOpt === 'auto') {
			parsedLabelPos = orient === 'horizontal' ? viewRect.y + viewRect.height / 2 < api.getHeight() / 2 ? '-' : '+' : viewRect.x + viewRect.width / 2 < api.getWidth() / 2 ? '+' : '-';
		} else if (isString(labelPosOpt)) {
			parsedLabelPos = {
				horizontal: {
					top: '-',
					bottom: '+'
				},
				vertical: {
					left: '-',
					right: '+'
				}
			}[orient][labelPosOpt];
		} else {
			// is number
			parsedLabelPos = labelPosOpt;
		}

		var labelAlignMap = {
			horizontal: 'center',
			vertical: parsedLabelPos >= 0 || parsedLabelPos === '+' ? 'left' : 'right'
		};
		var labelBaselineMap = {
			horizontal: parsedLabelPos >= 0 || parsedLabelPos === '+' ? 'top' : 'bottom',
			vertical: 'middle'
		};
		var rotationMap = {
			horizontal: 0,
			vertical: PI / 2
		}; // Position

		var mainLength = orient === 'vertical' ? viewRect.height : viewRect.width;
		var controlModel = timelineModel.getModel('controlStyle');
		var showControl = controlModel.get('show', true);
		var controlSize = showControl ? controlModel.get('itemSize') : 0;
		var controlGap = showControl ? controlModel.get('itemGap') : 0;
		var sizePlusGap = controlSize + controlGap; // Special label rotate.

		var labelRotation = timelineModel.get(['label', 'rotate']) || 0;
		labelRotation = labelRotation * PI / 180; // To radian.

		var playPosition;
		var prevBtnPosition;
		var nextBtnPosition;
		var controlPosition = controlModel.get('position', true);
		var showPlayBtn = showControl && controlModel.get('showPlayBtn', true);
		var showPrevBtn = showControl && controlModel.get('showPrevBtn', true);
		var showNextBtn = showControl && controlModel.get('showNextBtn', true);
		var xLeft = 0;
		var xRight = mainLength; // position[0] means left, position[1] means middle.

		if (controlPosition === 'left' || controlPosition === 'bottom') {
			showPlayBtn && (playPosition = [0, 0], xLeft += sizePlusGap);
			showPrevBtn && (prevBtnPosition = [xLeft, 0], xLeft += sizePlusGap);
			showNextBtn && (nextBtnPosition = [xRight - controlSize, 0], xRight -= sizePlusGap);
		} else {
			// 'top' 'right'
			showPlayBtn && (playPosition = [xRight - controlSize, 0], xRight -= sizePlusGap);
			showPrevBtn && (prevBtnPosition = [0, 0], xLeft += sizePlusGap);
			showNextBtn && (nextBtnPosition = [xRight - controlSize, 0], xRight -= sizePlusGap);
		}

		var axisExtent = [xLeft, xRight];

		if (timelineModel.get('inverse')) {
			axisExtent.reverse();
		}

		return {
			viewRect: viewRect,
			mainLength: mainLength,
			orient: orient,
			rotation: rotationMap[orient],
			labelRotation: labelRotation,
			labelPosOpt: parsedLabelPos,
			labelAlign: timelineModel.get(['label', 'align']) || labelAlignMap[orient],
			labelBaseline: timelineModel.get(['label', 'verticalAlign']) || timelineModel.get(['label', 'baseline']) || labelBaselineMap[orient],
			// Based on mainGroup.
			playPosition: playPosition,
			prevBtnPosition: prevBtnPosition,
			nextBtnPosition: nextBtnPosition,
			axisExtent: axisExtent,
			controlSize: controlSize,
			controlGap: controlGap
		};
	};

	SliderTimelineView.prototype._position = function (layoutInfo, timelineModel) {
		// Position is be called finally, because bounding rect is needed for
		// adapt content to fill viewRect (auto adapt offset).
		// Timeline may be not all in the viewRect when 'offset' is specified
		// as a number, because it is more appropriate that label aligns at
		// 'offset' but not the other edge defined by viewRect.
		var mainGroup = this._mainGroup;
		var labelGroup = this._labelGroup;
		var viewRect = layoutInfo.viewRect;

		if (layoutInfo.orient === 'vertical') {
			// transform to horizontal, inverse rotate by left-top point.
			var m = matrix.create();
			var rotateOriginX = viewRect.x;
			var rotateOriginY = viewRect.y + viewRect.height;
			matrix.translate(m, m, [-rotateOriginX, -rotateOriginY]);
			matrix.rotate(m, m, -PI / 2);
			matrix.translate(m, m, [rotateOriginX, rotateOriginY]);
			viewRect = viewRect.clone();
			viewRect.applyTransform(m);
		}

		var viewBound = getBound(viewRect);
		var mainBound = getBound(mainGroup.getBoundingRect());
		var labelBound = getBound(labelGroup.getBoundingRect());
		var mainPosition = [mainGroup.x, mainGroup.y];
		var labelsPosition = [labelGroup.x, labelGroup.y];
		labelsPosition[0] = mainPosition[0] = viewBound[0][0];
		var labelPosOpt = layoutInfo.labelPosOpt;

		if (labelPosOpt == null || isString(labelPosOpt)) {
			// '+' or '-'
			var mainBoundIdx = labelPosOpt === '+' ? 0 : 1;
			toBound(mainPosition, mainBound, viewBound, 1, mainBoundIdx);
			toBound(labelsPosition, labelBound, viewBound, 1, 1 - mainBoundIdx);
		} else {
			var mainBoundIdx = labelPosOpt >= 0 ? 0 : 1;
			toBound(mainPosition, mainBound, viewBound, 1, mainBoundIdx);
			labelsPosition[1] = mainPosition[1] + labelPosOpt;
		}

		mainGroup.setPosition(mainPosition);
		labelGroup.setPosition(labelsPosition);
		mainGroup.rotation = labelGroup.rotation = layoutInfo.rotation;
		setOrigin(mainGroup);
		setOrigin(labelGroup);

		function setOrigin(targetGroup) {
			targetGroup.originX = viewBound[0][0] - targetGroup.x;
			targetGroup.originY = viewBound[1][0] - targetGroup.y;
		}

		function getBound(rect) {
			// [[xmin, xmax], [ymin, ymax]]
			return [[rect.x, rect.x + rect.width], [rect.y, rect.y + rect.height]];
		}

		function toBound(fromPos, from, to, dimIdx, boundIdx) {
			fromPos[dimIdx] += to[dimIdx][boundIdx] - from[dimIdx][boundIdx];
		}
	};

	SliderTimelineView.prototype._createAxis = function (layoutInfo, timelineModel) {
		var data = timelineModel.getData();
		var axisType = timelineModel.get('axisType');
		var scale = createScaleByModel(timelineModel, axisType); // Customize scale. The `tickValue` is `dataIndex`.

		scale.getTicks = function () {
			return data.mapArray(['value'], function (value) {
				return {
					value: value
				};
			});
		};

		var dataExtent = data.getDataExtent('value');
		scale.setExtent(dataExtent[0], dataExtent[1]);
		scale.calcNiceTicks();
		var axis = new TimelineAxis('value', scale, layoutInfo.axisExtent, axisType);
		axis.model = timelineModel;
		return axis;
	};

	SliderTimelineView.prototype._createGroup = function (key) {
		var newGroup = this[key] = new graphic.Group();
		this.group.add(newGroup);
		return newGroup;
	};

	SliderTimelineView.prototype._renderAxisLine = function (layoutInfo, group, axis, timelineModel) {
		var axisExtent = axis.getExtent();

		if (!timelineModel.get(['lineStyle', 'show'])) {
			return;
		}

		var line = new graphic.Line({
			shape: {
				x1: axisExtent[0],
				y1: 0,
				x2: axisExtent[1],
				y2: 0
			},
			style: extend({
				lineCap: 'round'
			}, timelineModel.getModel('lineStyle').getLineStyle()),
			silent: true,
			z2: 1
		});
		group.add(line);
		var progressLine = this._progressLine = new graphic.Line({
			shape: {
				x1: axisExtent[0],
				x2: this._currentPointer ? this._currentPointer.x : axisExtent[0],
				y1: 0,
				y2: 0
			},
			style: defaults({
				lineCap: 'round',
				lineWidth: line.style.lineWidth
			}, timelineModel.getModel(['progress', 'lineStyle']).getLineStyle()),
			silent: true,
			z2: 1
		});
		group.add(progressLine);
	};

	SliderTimelineView.prototype._renderAxisTick = function (layoutInfo, group, axis, timelineModel) {
		var _this = this;

		var data = timelineModel.getData(); // Show all ticks, despite ignoring strategy.

		var ticks = axis.scale.getTicks();
		this._tickSymbols = []; // The value is dataIndex, see the customized scale.

		each(ticks, function (tick) {
			var tickCoord = axis.dataToCoord(tick.value);
			var itemModel = data.getItemModel(tick.value);
			var itemStyleModel = itemModel.getModel('itemStyle');
			var hoverStyleModel = itemModel.getModel(['emphasis', 'itemStyle']);
			var progressStyleModel = itemModel.getModel(['progress', 'itemStyle']);
			var symbolOpt = {
				x: tickCoord,
				y: 0,
				onclick: bind(_this._changeTimeline, _this, tick.value)
			};
			var el = giveSymbol(itemModel, itemStyleModel, group, symbolOpt);
			el.ensureState('emphasis').style = hoverStyleModel.getItemStyle();
			el.ensureState('progress').style = progressStyleModel.getItemStyle();
			enableHoverEmphasis(el);
			var ecData = getECData(el);

			if (itemModel.get('tooltip')) {
				ecData.dataIndex = tick.value;
				ecData.dataModel = timelineModel;
			} else {
				ecData.dataIndex = ecData.dataModel = null;
			}

			_this._tickSymbols.push(el);
		});
	};

	SliderTimelineView.prototype._renderAxisLabel = function (layoutInfo, group, axis, timelineModel) {
		var _this = this;

		var labelModel = axis.getLabelModel();

		if (!labelModel.get('show')) {
			return;
		}

		var data = timelineModel.getData();
		var labels = axis.getViewLabels();
		this._tickLabels = [];
		each(labels, function (labelItem) {
			// The tickValue is dataIndex, see the customized scale.
			var dataIndex = labelItem.tickValue;
			var itemModel = data.getItemModel(dataIndex);
			var normalLabelModel = itemModel.getModel('label');
			var hoverLabelModel = itemModel.getModel(['emphasis', 'label']);
			var progressLabelModel = itemModel.getModel(['progress', 'label']);
			var tickCoord = axis.dataToCoord(labelItem.tickValue);
			var textEl = new graphic.Text({
				x: tickCoord,
				y: 0,
				rotation: layoutInfo.labelRotation - layoutInfo.rotation,
				onclick: bind(_this._changeTimeline, _this, dataIndex),
				silent: false,
				style: createTextStyle(normalLabelModel, {
					text: labelItem.formattedLabel,
					align: layoutInfo.labelAlign,
					verticalAlign: layoutInfo.labelBaseline
				})
			});
			textEl.ensureState('emphasis').style = createTextStyle(hoverLabelModel);
			textEl.ensureState('progress').style = createTextStyle(progressLabelModel);
			group.add(textEl);
			enableHoverEmphasis(textEl);
			labelDataIndexStore(textEl).dataIndex = dataIndex;

			_this._tickLabels.push(textEl);
		});
	};

	SliderTimelineView.prototype._renderControl = function (layoutInfo, group, axis, timelineModel) {
		var controlSize = layoutInfo.controlSize;
		var rotation = layoutInfo.rotation;
		var itemStyle = timelineModel.getModel('controlStyle').getItemStyle();
		var hoverStyle = timelineModel.getModel(['emphasis', 'controlStyle']).getItemStyle();
		var playState = timelineModel.getPlayState();
		var inverse = timelineModel.get('inverse', true);
		makeBtn(layoutInfo.nextBtnPosition, 'next', bind(this._changeTimeline, this, inverse ? '-' : '+'));
		makeBtn(layoutInfo.prevBtnPosition, 'prev', bind(this._changeTimeline, this, inverse ? '+' : '-'));
		makeBtn(layoutInfo.playPosition, playState ? 'stop' : 'play', bind(this._handlePlayClick, this, !playState), true);

		function makeBtn(position, iconName, onclick, willRotate) {
			if (!position) {
				return;
			}

			var iconSize = parsePercent(retrieve2(timelineModel.get(['controlStyle', iconName + 'BtnSize']), controlSize), controlSize);
			var rect = [0, -iconSize / 2, iconSize, iconSize];
			var btn = makeControlIcon(timelineModel, iconName + 'Icon', rect, {
				x: position[0],
				y: position[1],
				originX: controlSize / 2,
				originY: 0,
				rotation: willRotate ? -rotation : 0,
				rectHover: true,
				style: itemStyle,
				onclick: onclick
			});
			btn.ensureState('emphasis').style = hoverStyle;
			group.add(btn);
			enableHoverEmphasis(btn);
		}
	};

	SliderTimelineView.prototype._renderCurrentPointer = function (layoutInfo, group, axis, timelineModel) {
		var data = timelineModel.getData();
		var currentIndex = timelineModel.getCurrentIndex();
		var pointerModel = data.getItemModel(currentIndex).getModel('checkpointStyle');
		var me = this;
		var callback = {
			onCreate: function (pointer) {
				pointer.draggable = true;
				pointer.drift = bind(me._handlePointerDrag, me);
				pointer.ondragend = bind(me._handlePointerDragend, me);
				pointerMoveTo(pointer, me._progressLine, currentIndex, axis, timelineModel, true);
			},
			onUpdate: function (pointer) {
				pointerMoveTo(pointer, me._progressLine, currentIndex, axis, timelineModel);
			}
		}; // Reuse when exists, for animation and drag.

		this._currentPointer = giveSymbol(pointerModel, pointerModel, this._mainGroup, {}, this._currentPointer, callback);
	};

	SliderTimelineView.prototype._handlePlayClick = function (nextState) {
		this._clearTimer();

		this.api.dispatchAction({
			type: 'timelinePlayChange',
			playState: nextState,
			from: this.uid
		});
	};

	SliderTimelineView.prototype._handlePointerDrag = function (dx, dy, e) {
		this._clearTimer();

		this._pointerChangeTimeline([e.offsetX, e.offsetY]);
	};

	SliderTimelineView.prototype._handlePointerDragend = function (e) {
		this._pointerChangeTimeline([e.offsetX, e.offsetY], true);
	};

	SliderTimelineView.prototype._pointerChangeTimeline = function (mousePos, trigger) {
		var toCoord = this._toAxisCoord(mousePos)[0];

		var axis = this._axis;
		var axisExtent = numberUtil.asc(axis.getExtent().slice());
		toCoord > axisExtent[1] && (toCoord = axisExtent[1]);
		toCoord < axisExtent[0] && (toCoord = axisExtent[0]);
		this._currentPointer.x = toCoord;

		this._currentPointer.markRedraw();

		var progressLine = this._progressLine;

		if (progressLine) {
			progressLine.shape.x2 = toCoord;
			progressLine.dirty();
		}

		var targetDataIndex = this._findNearestTick(toCoord);

		var timelineModel = this.model;

		if (trigger || targetDataIndex !== timelineModel.getCurrentIndex() && timelineModel.get('realtime')) {
			this._changeTimeline(targetDataIndex);
		}
	};

	SliderTimelineView.prototype._doPlayStop = function () {
		var _this = this;

		this._clearTimer();

		if (this.model.getPlayState()) {
			this._timer = setTimeout(function () {
				// Do not cache
				var timelineModel = _this.model;

				_this._changeTimeline(timelineModel.getCurrentIndex() + (timelineModel.get('rewind', true) ? -1 : 1));
			}, this.model.get('playInterval'));
		}
	};

	SliderTimelineView.prototype._toAxisCoord = function (vertex) {
		var trans = this._mainGroup.getLocalTransform();

		return graphic.applyTransform(vertex, trans, true);
	};

	SliderTimelineView.prototype._findNearestTick = function (axisCoord) {
		var data = this.model.getData();
		var dist = Infinity;
		var targetDataIndex;
		var axis = this._axis;
		data.each(['value'], function (value, dataIndex) {
			var coord = axis.dataToCoord(value);
			var d = Math.abs(coord - axisCoord);

			if (d < dist) {
				dist = d;
				targetDataIndex = dataIndex;
			}
		});
		return targetDataIndex;
	};

	SliderTimelineView.prototype._clearTimer = function () {
		if (this._timer) {
			clearTimeout(this._timer);
			this._timer = null;
		}
	};

	SliderTimelineView.prototype._changeTimeline = function (nextIndex) {
		var currentIndex = this.model.getCurrentIndex();

		if (nextIndex === '+') {
			nextIndex = currentIndex + 1;
		} else if (nextIndex === '-') {
			nextIndex = currentIndex - 1;
		}

		this.api.dispatchAction({
			type: 'timelineChange',
			currentIndex: nextIndex,
			from: this.uid
		});
	};

	SliderTimelineView.prototype._updateTicksStatus = function () {
		var currentIndex = this.model.getCurrentIndex();
		var tickSymbols = this._tickSymbols;
		var tickLabels = this._tickLabels;

		if (tickSymbols) {
			for (var i = 0; i < tickSymbols.length; i++) {
				tickSymbols && tickSymbols[i] && tickSymbols[i].toggleState('progress', i < currentIndex);
			}
		}

		if (tickLabels) {
			for (var i = 0; i < tickLabels.length; i++) {
				tickLabels && tickLabels[i] && tickLabels[i].toggleState('progress', labelDataIndexStore(tickLabels[i]).dataIndex <= currentIndex);
			}
		}
	};

	SliderTimelineView.type = 'timeline.slider';
	return SliderTimelineView;
}(TimelineView);

function createScaleByModel(model, axisType) {
	axisType = axisType || model.get('type');

	if (axisType) {
		switch (axisType) {
		// Buildin scale
		case 'category':
			return new OrdinalScale({
				ordinalMeta: model.getCategories(),
				extent: [Infinity, -Infinity]
			});

		case 'time':
			return new TimeScale({
				locale: model.ecModel.getLocaleModel(),
				useUTC: model.ecModel.get('useUTC')
			});

		default:
			// default to be value
			return new IntervalScale();
		}
	}
}

function getViewRect(model, api) {
	return layout.getLayoutRect(model.getBoxLayoutParams(), {
		width: api.getWidth(),
		height: api.getHeight()
	}, model.get('padding'));
}

function makeControlIcon(timelineModel, objPath, rect, opts) {
	var style = opts.style;
	var icon = graphic.createIcon(timelineModel.get(['controlStyle', objPath]), opts || {}, new BoundingRect(rect[0], rect[1], rect[2], rect[3])); // TODO createIcon won't use style in opt.

	if (style) {
		icon.setStyle(style);
	}

	return icon;
}
/**
 * Create symbol or update symbol
 * opt: basic position and event handlers
 */

function giveSymbol(hostModel, itemStyleModel, group, opt, symbol, callback) {
	var color = itemStyleModel.get('color');

	if (!symbol) {
		var symbolType = hostModel.get('symbol');
		symbol = createSymbol(symbolType, -1, -1, 2, 2, color);
		symbol.setStyle('strokeNoScale', true);
		group.add(symbol);
		callback && callback.onCreate(symbol);
	} else {
		symbol.setColor(color);
		group.add(symbol); // Group may be new, also need to add.

		callback && callback.onUpdate(symbol);
	} // Style

	var itemStyle = itemStyleModel.getItemStyle(['color']);
	symbol.setStyle(itemStyle); // Transform and events.

	opt = merge({
		rectHover: true,
		z2: 100
	}, opt, true);
	var symbolSize = normalizeSymbolSize(hostModel.get('symbolSize'));
	opt.scaleX = symbolSize[0] / 2;
	opt.scaleY = symbolSize[1] / 2;
	var symbolOffset = normalizeSymbolOffset(hostModel.get('symbolOffset'), symbolSize);

	if (symbolOffset) {
		opt.x = (opt.x || 0) + symbolOffset[0];
		opt.y = (opt.y || 0) + symbolOffset[1];
	}

	var symbolRotate = hostModel.get('symbolRotate');
	opt.rotation = (symbolRotate || 0) * Math.PI / 180 || 0;
	symbol.attr(opt); // FIXME
	// (1) When symbol.style.strokeNoScale is true and updateTransform is not performed,
	// getBoundingRect will return wrong result.
	// (This is supposed to be resolved in zrender, but it is a little difficult to
	// leverage performance and auto updateTransform)
	// (2) All of ancesters of symbol do not scale, so we can just updateTransform symbol.

	symbol.updateTransform();
	return symbol;
}

function pointerMoveTo(pointer, progressLine, dataIndex, axis, timelineModel, noAnimation) {
	if (pointer.dragging) {
		return;
	}

	var pointerModel = timelineModel.getModel('checkpointStyle');
	var toCoord = axis.dataToCoord(timelineModel.getData().get('value', dataIndex));

	if (noAnimation || !pointerModel.get('animation', true)) {
		pointer.attr({
			x: toCoord,
			y: 0
		});
		progressLine && progressLine.attr({
			shape: {
				x2: toCoord
			}
		});
	} else {
		var animationCfg = {
			duration: pointerModel.get('animationDuration', true),
			easing: pointerModel.get('animationEasing', true)
		};
		pointer.stopAnimation(null, true);
		pointer.animateTo({
			x: toCoord,
			y: 0
		}, animationCfg);
		progressLine && progressLine.animateTo({
			shape: {
				x2: toCoord
			}
		}, animationCfg);
	}
}

export default SliderTimelineView;